using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
using System.Reflection;
using System.ComponentModel;
using System.Web.UI;
using System.Runtime.InteropServices;
using NBusiness.Utilities;
using System.Collections;
using System.Drawing;
using System.Collections.ObjectModel;
using NBusiness.Data.Web.Design;

namespace NBusiness.Data.Web
{
    [ComVisible(false)]
    //[Designer(typeof(ScaffoldingDesigner))]
    public class Scaffolding : CompositeDataBoundControl
    {
        #region Member fields and properties
        public string ReflectionPath
        {
            get
            {
                return (string)ViewState[this.ID + "_ReflectionTrail"];
            }
            set
            {
                ViewState[this.ID + "_ReflectionTrail"] = value;
            }
        }

        public EntityBase EditItem
        {
            get
            {
                return (EntityBase)ViewState[this.ID + "_EditItem"];
            }
            set
            {
                ViewState[this.ID + "_EditItem"] = value;
            }
        }

        public IEntityCollection BaseItem
        {
            get
            {
                return (IEntityCollection)ViewState[this.ID + "_BaseItem"];
            }
            set
            {
                ViewState[this.ID + "_BaseItem"] = value;
            }
        } 

        private Collection<ValidationRule> _brokenRules = null;
        #endregion

        #region Styles and colors
        //http://www.colorcombos.com/combotester.html?color0=5c93Ce&color1=033466&color2=F0f0e0&color3=151f44
        private static Color Hex1 = Color.FromArgb(92, 147, 206);
        private static Color Hex2 = Color.FromArgb(3, 52, 102);
        private static Color Hex3 = Color.FromArgb(240, 240, 224);
        private static Color Hex4 = Color.FromArgb(21, 31, 68);
        private static Color Hex5 = Color.FromArgb(144, 170, 209);
        private static Color Hex6 = Color.Black;
        
        #region Errors
        private string _errorTableCssClass;
        [Category("Styles")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public string ErrorTableCssClass
        {
            get { return _errorTableCssClass; }
            set { _errorTableCssClass = value; }
        }
        private Color _errorTableBorderColor = Color.FromArgb(216, 9, 9);
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color ErrorTableBorderColor
        {
            get { return _errorTableBorderColor; }
            set { _errorTableBorderColor = value; }
        }
        private Color _errorTableBackgroundColor = Color.FromArgb(253, 186, 186);
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color ErrorTableBackgroundColor
        {
            get { return _errorTableBackgroundColor; }
            set { _errorTableBackgroundColor = value; }
        } 
        #endregion        

        #region Navigation
        private string _navigateCssClass;
        [Category("Styles")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public string NavigateCssClass
        {
            get { return _navigateCssClass; }
            set { _navigateCssClass = value; }
        }
        private Color _navigateBorderColor = Hex2;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color NavigateBorderColor
        {
            get { return _navigateBorderColor; }
            set { _navigateBorderColor = value; }
        }
        private Color _navigateBackgroundColor = Hex3;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color NavigateBackgroundColor
        {
            get { return _navigateBackgroundColor; }
            set { _navigateBackgroundColor = value; }
        }
        #endregion

        #region Header
        private string _headerCssClass;
        [Category("Styles")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public string HeaderCssClass
        {
            get { return _headerCssClass; }
            set { _headerCssClass = value; }
        }

        private Color _headerBorderColor = Hex4;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color HeaderBorderColor
        {
            get { return _headerBorderColor; }
            set { _headerBorderColor = value; }
        }

        private Color _headerBackgroundColor = Hex5;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color HeaderBackgroundColor
        {
            get { return _headerBackgroundColor; }
            set { _headerBackgroundColor = value; }
        }
        #endregion

        #region Rows
        private string _rowCssClass;
        [Category("Styles")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public string RowCssClass
        {
            get { return _rowCssClass; }
            set { _rowCssClass = value; }
        }
        private Color _rowBorderColor = Hex3;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color RowBorderColor
        {
            get { return _rowBorderColor; }
            set { _rowBorderColor = value; }
        }
        private Color _rowBackgroundColor = Hex3;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color RowBackgroundColor
        {
            get { return _rowBackgroundColor; }
            set { _rowBackgroundColor = value; }
        }

        private string _alternatingRowCssClass;
        [Category("Styles")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public string AlternatingRowCssClass
        {
            get { return _alternatingRowCssClass; }
            set { _alternatingRowCssClass = value; }
        }

        private Color _alternatingRowBorderColor = Hex1;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color AlternatingRowBorderColor
        {
            get { return _alternatingRowBorderColor; }
            set { _alternatingRowBorderColor = value; }
        }
        private Color _alternatingRowBackgroundColor = Hex1;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color AlternatingRowBackgroundColor
        {
            get { return _alternatingRowBackgroundColor; }
            set { _alternatingRowBackgroundColor = value; }
        }
        #endregion

        #region Commands
        private string _commandButtonCssClass;
        [Category("Styles")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public string CommandButtonCssClass
        {
            get { return _commandButtonCssClass; }
            set { _commandButtonCssClass = value; }
        }
        private Color _commandButtonBorderColor = Hex4;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color CommandButtonBorderColor
        {
            get { return _commandButtonBorderColor; }
            set { _commandButtonBorderColor = value; }
        }
        private Color _commandButtonBackgroundColor = Hex3;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color CommandButtonBackgroundColor
        {
            get { return _commandButtonBackgroundColor; }
            set { _commandButtonBackgroundColor = value; }
        }

        private string _relationshipCssClass;
        [Category("Styles")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public string RelationshipCssClass
        {
            get { return _relationshipCssClass; }
            set { _relationshipCssClass = value; }
        }

        private Color _relationshipBorderColor = Hex4;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color RelationshipBorderColor
        {
            get { return _relationshipBorderColor; }
            set { _relationshipBorderColor = value; }
        }
        private Color _relationshipBackgroundColor = Hex3;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color RelationshipBackgroundColor
        {
            get { return _relationshipBackgroundColor; }
            set { _relationshipBackgroundColor = value; }
        }

        private string _editRowCssClass;
        [Category("Styles")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public string EditRowCssClass
        {
            get { return _editRowCssClass; }
            set { _editRowCssClass = value; }
        }

        private Color _editRowBorderColor = Hex6;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color EditRowBorderColor
        {
            get { return _editRowBorderColor; }
            set { _editRowBorderColor = value; }
        }
        private Color _editRowBackgroundColor = Hex5;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color EditRowBackgroundColor
        {
            get { return _editRowBackgroundColor; }
            set { _editRowBackgroundColor = value; }
        }

        private string _addRowCssClass;
        [Category("Styles")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public string AddRowCssClass
        {
            get { return _addRowCssClass; }
            set { _addRowCssClass = value; }
        }
        private Color _addRowBorderColor = Hex4;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color AddRowBorderColor
        {
            get { return _addRowBorderColor; }
            set { _addRowBorderColor = value; }
        }
        private Color _addRowBackgroundColor = Hex5;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color AddRowBackgroundColor
        {
            get { return _addRowBackgroundColor; }
            set { _addRowBackgroundColor = value; }
        }
        #endregion

        #region Paging
        private Color _pagerBackgroundColor;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color PagerBackgroundColor
        {
            get { return _pagerBackgroundColor; }
            set { _pagerBackgroundColor = value; }
        }
        private Color _pagerBorderColor = Hex6;
        [Category("Colors")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public Color PagerBorderColor
        {
            get { return _pagerBorderColor; }
            set { _pagerBorderColor = value; }
        }
        private string _pagerCssClass;
        [Category("Styles")]
        [PersistenceMode(PersistenceMode.Attribute)]
        public string PagerCssClass
        {
            get { return _pagerCssClass; }
            set { _pagerCssClass = value; }
        }
        #endregion
        #endregion

        #region Calculated Properties
        private Type BaseEntityType
        {
            get
            {
                EntityDataSource eds = this.GetDataSource() as EntityDataSource;
                Type baseEntityType = null;
                if (eds != null)
                {
                    string assemblyName = eds.AssemblyName;
                    string typeName = eds.TypeName;
                    Reflection.TryLoadType(assemblyName, typeName, out baseEntityType);
                }
                return baseEntityType;
            }
        }
        /// <summary>
        /// This will actually drill down into an EntityCollection based on the
        /// BaseItem and the reflection path.
        /// </summary>
        private IEntityCollection CurrentEntities
        {
            get
            {
                IEntityCollection entityCollection = BaseItem;
                EntityBase entity = null;

                if (ReflectionPath != null)
                {
                    string[] properties = ReflectionPath.Split('.');
                    foreach (string property in properties)
                    {
                        if (property.StartsWith("["))
                        {
                            int i = Convert.ToInt32(property.Trim('[', ']'));
                            entity = entityCollection[i];
                        }
                        else if (property.Contains("["))
                        {
                            string[] split = property.Split('[');
                            string prop = split[0];
                            int i = Convert.ToInt32(split[1].Trim('[', ']'));

                            PropertyInfo pi = entity.GetType().GetProperty(prop);
                            entityCollection = (IEntityCollection)pi.GetValue(entity, null);
                            entity = entityCollection[i];
                        }
                        else
                        {
                            PropertyInfo pi = entity.GetType().GetProperty(property);
                            entityCollection = (IEntityCollection)pi.GetValue(entity, null);
                        }

                        if (entityCollection == null || entity == null) break;
                    }
                    PageCount = entityCollection.Count / PageSize;
                }

                return entityCollection;
            }
        }

        /// <summary>
        /// Here we're using the BaseEntityType and the ReflectionPath to
        /// drill down to the current entity Type. This is used for things like
        /// building the column headers and the add row based on reflection. 
        /// You need this because if there are no entities in the data 
        /// collection then you still want to build the column headers.
        /// </summary>
        private Type CurrentEntityType
        {
            get
            {
                Type entityType = BaseEntityType;

                if (entityType != null && ReflectionPath != null)
                {
                    string[] properties = ReflectionPath.Split('.');
                    foreach (string property in properties)
                    {
                        if (property.StartsWith("["))
                        {
                            if (!typeof(IEntityCollection).IsAssignableFrom(entityType))
                                throw new ScaffoldingException(Properties.Resources.ScaffoldingReflectionNotACollection);

                            entityType = entityType.BaseType.GetGenericArguments()[0];
                        }
                        else if (property.Contains("["))
                        {
                            string prop = property.Substring(0, property.IndexOf('['));

                            PropertyInfo pi = entityType.GetProperty(prop);
                            entityType = pi.PropertyType;

                            if (typeof(IEntityCollection).IsAssignableFrom(entityType.BaseType))
                                entityType = entityType.BaseType.GetGenericArguments()[0];
                        }
                        else
                        {
                            PropertyInfo pi = entityType.GetProperty(property);
                            entityType = pi.PropertyType;
                        }
                    }
                }

                if (typeof(IEntityCollection).IsAssignableFrom(entityType.BaseType))
                    entityType = entityType.BaseType.GetGenericArguments()[0];
    
                return entityType;
            }
        }
        #endregion

        #region Paging
        private bool _allowPaging = false;
        [Category("Paging")]
        [DefaultValue(false)]
        [PersistenceMode(PersistenceMode.Attribute)]
        public bool AllowPaging
        {
            get { return _allowPaging; }
            set { _allowPaging = value; }
        }
        private int _pageSize = 10;
        [Category("Paging")]
        [DefaultValue(10)]
        [PersistenceMode(PersistenceMode.Attribute)]
        public int PageSize
        {
            get { return _pageSize; }
            set { _pageSize = value; }
        }
        private int _pageWindow = 8;
        [Category("Paging")]
        [DefaultValue(10)]
        [PersistenceMode(PersistenceMode.Attribute)]
        public int PageWindow
        {
            get { return _pageWindow; }
            set { _pageWindow = value; }
        }
        [Category("Paging")]
        [DefaultValue(0)]
        [Browsable(false)]
        public int PageIndex
        {
            get 
            {
                return (ViewState[this.ID + "_PageIndex"] == null ?
                    0 : (int)ViewState[this.ID + "_PageIndex"]);
            }
            set 
            { 
                ViewState[this.ID + "_PageIndex"] = value;
            }
        }

        [Browsable(false)]
        public int PageCount
        {
            get 
            { 
                return (ViewState[this.ID + "_PageCount"]==null?
                    1 : (int)ViewState[this.ID + "_PageCount"]); 
            }
            protected set { ViewState[this.ID + "_PageCount"] = value; }
        }

        protected override DataSourceSelectArguments CreateDataSourceSelectArguments()
        {
            DataSourceSelectArguments args = new DataSourceSelectArguments();
            DataSourceView view = GetData();
            bool canPage = AllowPaging && view.CanPage;

            if (canPage)
            {
                args.RetrieveTotalRowCount = true;
                args.MaximumRows = PageSize;
                args.StartRowIndex = PageSize * PageIndex;
            }
            return args;
        }
        #endregion

        #region CreateChildControls
        protected override int CreateChildControls(System.Collections.IEnumerable dataSource, bool dataBinding)
        {
            #region Build items list if dataBinding
            if (dataBinding && dataSource != null && dataSource is IEntityCollection)
            {
                BaseItem = dataSource as IEntityCollection;
                
                DataSourceView view = GetData();
                if (AllowPaging && view.CanPage)
                {
                    PageCount = (SelectArguments.TotalRowCount / PageSize) + 
                        (SelectArguments.TotalRowCount % PageSize == 0 ? 0 : 1);

                    PageIndex = 0;
                }
            }
            #endregion

            int returnCount = 0;
            if (this.DesignMode)
            {
                Label l = new Label();
                l.Text = "Design time...";
                l.BorderColor = Hex4;
                l.BackColor = Hex3;
                Controls.Add(l);
            }
            else
            {
                BuildErrorTable();
                BuildNavigationRow();

                Table table = new Table();
                Type currentEntityType = CurrentEntityType;
                BuildColumnHeaders(table, currentEntityType);
                returnCount = BuildRows(table);
                BuildAddRow(currentEntityType, table);
                Controls.Add(table);

                if(AllowPaging)
                    BuildPageRow(currentEntityType);
            }

            return returnCount;
        }        

        #region Errors
        private void BuildErrorTable()
        {
            if (_brokenRules != null && _brokenRules.Count > 0)
            {
                Table errorTable = new Table();
                
                if(!string.IsNullOrEmpty(ErrorTableCssClass))
                {
                    errorTable.CssClass = ErrorTableCssClass;
                }
                else 
                {
                    errorTable.BorderColor = Color.Red;
                    errorTable.BackColor = Color.LightPink;
                    errorTable.BorderWidth = new Unit("1px");
                    errorTable.BorderStyle = BorderStyle.Solid;
                    errorTable.Width = new Unit("100%");
                }

                foreach (ValidationRule rule in _brokenRules)
                {
                    TableRow errorRow = new TableRow();

                    TableCell errorCell = new TableCell();

                    Label errorLabel = new Label();
                    errorLabel.Text = "* " + rule.ErrorMessage + " : " + rule.Field;

                    errorCell.Controls.Add(errorLabel);
                    errorRow.Cells.Add(errorCell);
                    errorTable.Rows.Add(errorRow);
                }
                Controls.Add(errorTable);
            }
        }
        #endregion

        #region Build navigation row
        private void BuildNavigationRow()
        {
            if (!string.IsNullOrEmpty(ReflectionPath))
            {
                Panel p = new Panel();
                Table table = new Table();
                p.Controls.Add(table);
                Controls.Add(p);

                if (!string.IsNullOrEmpty(NavigateCssClass))
                {
                    p.CssClass = NavigateCssClass;
                }
                else
                {
                    p.Width = new Unit("100%");
                    p.BorderColor = NavigateBorderColor;
                    p.BackColor = NavigateBackgroundColor;
                    p.BorderStyle = BorderStyle.Solid;
                    p.BorderWidth = new Unit("1px");
                    p.HorizontalAlign = HorizontalAlign.Left;
                }

                TableRow navigationRow = new TableRow();

                TableCell navigationCell = new TableCell();
                navigationCell.BorderStyle = BorderStyle.None;

                Label navigationLabel = new Label();
                navigationLabel.Text = Properties.Resources.ScaffoldingNavigationText;
                
                TableCell reflectionPathCell = new TableCell();
                reflectionPathCell.BorderStyle = BorderStyle.None;
                
                Label reflectionPathLabel = new Label();
                reflectionPathLabel.Text = ReflectionPath;
                
                TableCell navigateBackCell = new TableCell();
                navigateBackCell.BorderStyle = BorderStyle.None;
                
                LinkButton navigateBack = new LinkButton();
                navigateBack.Text = Properties.Resources.ScaffoldingNavigateBack;
                navigateBack.Click += new EventHandler(navigateBack_Click);

                navigateBackCell.Controls.Add(navigateBack);
                reflectionPathCell.Controls.Add(reflectionPathLabel);
                navigationCell.Controls.Add(navigationLabel);

                navigationRow.Cells.Add(navigationCell);
                navigationRow.Cells.Add(reflectionPathCell);
                navigationRow.Cells.Add(navigateBackCell);
                table.Rows.Add(navigationRow);
            }
        }
        #endregion
        
        #region Build column headers
        private void BuildColumnHeaders(Table table, Type currentEntityType)
        {
            TableRow headerRow = new TableRow();
            if (!string.IsNullOrEmpty(HeaderCssClass))
            {
                headerRow.CssClass = HeaderCssClass;
            }
            else
            {
                table.BorderWidth = new Unit("1px");
                table.Width = new Unit("100%");

                headerRow.BorderColor = HeaderBorderColor;
                headerRow.BackColor = HeaderBackgroundColor;
                headerRow.BorderStyle = BorderStyle.Solid;
                headerRow.BorderWidth = new Unit("1px");
            }

            foreach (PropertyInfo field in currentEntityType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                object[] bindable = field.GetCustomAttributes(typeof(BindableAttribute), false);
                if (bindable.Length > 0 &&
                    (bindable[0] as BindableAttribute).Bindable == true)
                {
                    object[] dataObjectField = field.GetCustomAttributes(typeof(DataObjectFieldAttribute), false);
                    bool isKey = (dataObjectField.Length > 0 && (dataObjectField[0] as DataObjectFieldAttribute).PrimaryKey);

                    TableCell cell = new TableCell();
                    cell.BorderStyle = BorderStyle.None;
                    cell.BorderWidth = new Unit("0px");

                    Label l = new Label();
                    l.Text = field.Name;
                    cell.Controls.Add(l);
                    headerRow.Cells.Add(cell);
                }
            }
            table.Rows.Add(headerRow);
        }
        #endregion

        #region Build rows
        private int BuildRows(Table table)
        {
            IEntityCollection currentEntities = CurrentEntities;
            if (currentEntities.Count > 0)
            {
                //Assume DAL paging was performed.
                int index = 0;
                int cnt = currentEntities.Count;

                //If the number of entities is the same as the TotalRowCount
                //then either paging wasn't performed or it's less than the 
                //page size. Try to do serverside paging here.
                if (AllowPaging && cnt >= PageSize)
                {
                    index = PageIndex * PageSize;
                    cnt = PageSize;
                }

                for (   int x = index; 
                        x < index + cnt && 
                        x < currentEntities.Count; 
                        x++)
                {
                    EntityBase obj = currentEntities[x];

                    bool editRow = (EditItem != null && obj.Equals(EditItem));

                    TableRow row = new TableRow();

                    //Call this everytime so that the alternating continues correctly.
                    #region Set Row Styles
                    if (x % 2 == 1)
                    {
                        if (!string.IsNullOrEmpty(AlternatingRowCssClass))
                        {
                            row.CssClass = AlternatingRowCssClass;
                        }
                        else
                        {
                            row.BackColor = AlternatingRowBackgroundColor;
                            row.BorderWidth = new Unit("0px");
                        }
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(RowCssClass))
                        {
                            row.CssClass = RowCssClass;
                        }
                        else
                        {
                            row.BackColor = RowBackgroundColor;
                            row.BorderWidth = new Unit("0px");
                        }
                    }

                    if (editRow)
                    {
                        if (!string.IsNullOrEmpty(EditRowCssClass))
                        {
                            row.CssClass = EditRowCssClass;
                        }
                        else
                        {
                            row.BackColor = EditRowBackgroundColor;
                            row.BorderColor = EditRowBorderColor;
                            row.BorderStyle = BorderStyle.Solid;
                            row.BorderWidth = new Unit("1px");
                        }
                    }
                    #endregion

                    Dictionary<string, string> keys = new Dictionary<string, string>();
                    List<LinkButton> relativeButtons = new List<LinkButton>();

                    #region Build Controls for Bindable Properties
                    foreach (PropertyInfo field in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
                    {
                        object[] bindable = field.GetCustomAttributes(typeof(BindableAttribute), false);
                        if (bindable.Length > 0 &&
                            (bindable[0] as BindableAttribute).Bindable == true)
                        {
                            object[] dataObjectField = field.GetCustomAttributes(typeof(DataObjectFieldAttribute), false);
                            bool isKey = (dataObjectField.Length > 0 && (dataObjectField[0] as DataObjectFieldAttribute).PrimaryKey);

                            TableCell cell = new TableCell();
                            cell.BorderStyle = BorderStyle.None;
                            cell.BorderWidth = new Unit("0px");
                            object data = field.GetValue(obj, null);

                            //Edit row controls use a text box.
                            Control c = null;
                            if (!isKey && editRow)
                            {
                                c = new TextBox();
                                (c as TextBox).Text = data.ToString();
                            }
                            else
                            {
                                c = new Label();
                                (c as Label).Text = data.ToString();
                            }
                            cell.Controls.Add(c);
                            row.Cells.Add(cell);

                            if (isKey)
                            {
                                keys.Add(field.Name, data.ToString());
                            }
                        }
                        else if (typeof(IEntityCollection).IsAssignableFrom(field.PropertyType))
                        {
                            TableCell cell = new TableCell();
                            if (!string.IsNullOrEmpty(EditRowCssClass))
                            {
                                cell.CssClass = RelationshipCssClass;
                            }
                            else
                            {
                                cell.BackColor = RelationshipBackgroundColor;
                                cell.BorderColor = RelationshipBorderColor;
                                cell.BorderStyle = BorderStyle.Solid;
                                cell.BorderWidth = new Unit(1);
                            }

                            Type entityType = field.PropertyType.BaseType.GetGenericArguments()[0];
                            if (EntityBase.CanFetchEntity(entityType))
                            {
                                LinkButton editRelative = new LinkButton();
                                editRelative.Text = field.Name;
                                editRelative.CommandName = "relative";
                                editRelative.CommandArgument = field.Name;
                                editRelative.Command += new CommandEventHandler(editRelative_Command);

                                relativeButtons.Add(editRelative);
                                cell.Controls.Add(editRelative);
                            }
                            else
                            {
                                Label relative = new Label();
                                relative.Text = field.Name;
                                cell.Controls.Add(relative);
                            }
                            row.Cells.Add(cell);
                        }
                    }
                    #endregion

                    #region Build command arguments from primary keys
                    StringBuilder parameters = new StringBuilder();
                    foreach (string key in keys.Keys)
                    {
                        if (parameters.Length > 0)
                        {
                            parameters.Append("&");
                            row.ID += "_";
                        }

                        parameters.Append(key + "=" + keys[key]);
                        row.ID += keys[key];
                    }
                    #endregion

                    #region Edit relative buttons
                    foreach (LinkButton lb in relativeButtons)
                    {
                        lb.CommandArgument = parameters.ToString() + "&r=" + lb.CommandArgument;
                    }
                    #endregion

                    #region Build Edit / Delete buttons
                    //Build controls if this is the Edit row.
                    if (editRow && obj.CanUpdate())
                    {
                        TableCell updateCell = new TableCell();
                        if (!string.IsNullOrEmpty(CommandButtonCssClass))
                        {
                            updateCell.CssClass = CommandButtonCssClass;
                        }
                        else
                        {
                            updateCell.BorderStyle = BorderStyle.Solid;
                            updateCell.BorderColor = CommandButtonBorderColor;
                            updateCell.BackColor = CommandButtonBackgroundColor;
                            updateCell.BorderWidth = new Unit(1);
                        }

                        LinkButton updateButton = new LinkButton();
                        updateButton.Command += new CommandEventHandler(updateButton_Command);
                        updateButton.Text = Properties.Resources.ScaffoldingUpdate;
                        updateButton.CommandArgument = parameters.ToString();
                        updateButton.CommandName = "update";
                        updateCell.Controls.Add(updateButton);
                        row.Cells.Add(updateCell);

                        TableCell cancelCell = new TableCell();
                        if (!string.IsNullOrEmpty(CommandButtonCssClass))
                        {
                            cancelCell.CssClass = CommandButtonCssClass;
                        }
                        else
                        {
                            cancelCell.BorderStyle = BorderStyle.Solid;
                            cancelCell.BorderColor = CommandButtonBorderColor;
                            cancelCell.BackColor = CommandButtonBackgroundColor;
                            cancelCell.BorderWidth = new Unit(1);
                        }

                        LinkButton cancelButton = new LinkButton();
                        cancelButton.Command += new CommandEventHandler(cancelButton_Command);
                        cancelButton.Text = Properties.Resources.ScaffoldingCancel;
                        cancelButton.CommandArgument = parameters.ToString();
                        cancelButton.CommandName = "cancel";
                        cancelCell.Controls.Add(cancelButton);
                        row.Cells.Add(cancelCell);
                    }
                    else //build controls for a regular row.
                    {
                        TableCell editCell = new TableCell();
                        if (!string.IsNullOrEmpty(CommandButtonCssClass))
                        {
                            editCell.CssClass = CommandButtonCssClass;
                        }
                        else
                        {
                            editCell.BorderStyle = BorderStyle.Solid;
                            editCell.BorderColor = CommandButtonBorderColor;
                            editCell.BackColor = CommandButtonBackgroundColor;
                            editCell.BorderWidth = new Unit(1);
                        }

                        if (obj.CanUpdate())
                        {
                            LinkButton editButton = new LinkButton();
                            editButton.Command += new CommandEventHandler(editButton_Command);
                            editButton.Text = Properties.Resources.ScaffoldingEdit;
                            editButton.CommandArgument = parameters.ToString();
                            editButton.CommandName = "edit";
                            editCell.Controls.Add(editButton);
                            editCell.BorderWidth = new Unit(1);
                        }
                        else
                        {
                            Label l = new Label();
                            l.Text = Properties.Resources.ScaffoldingEdit;
                            editCell.Controls.Add(l);
                        }
                        row.Cells.Add(editCell);

                        TableCell deleteCell = new TableCell();
                        if (!string.IsNullOrEmpty(CommandButtonCssClass))
                        {
                            deleteCell.CssClass = CommandButtonCssClass;
                        }
                        else
                        {
                            deleteCell.BorderStyle = BorderStyle.Solid;
                            deleteCell.BorderColor = CommandButtonBorderColor;
                            deleteCell.BackColor = CommandButtonBackgroundColor;
                            deleteCell.BorderWidth = new Unit(1);
                        }

                        if (obj.CanDelete())
                        {
                            LinkButton deleteButton = new LinkButton();
                            deleteButton.Command += new CommandEventHandler(deleteButton_Command);
                            deleteButton.Text = Properties.Resources.ScaffoldingDelete;
                            deleteButton.CommandArgument = parameters.ToString();
                            deleteButton.CommandName = "delete";
                            deleteButton.OnClientClick =
                                string.Format("return confirm('{0}');",
                                Properties.Resources.ScaffoldingConfirmDelete);

                            deleteCell.Controls.Add(deleteButton);
                        }
                        else
                        {
                            Label l = new Label();
                            l.Text = Properties.Resources.ScaffoldingDelete;
                            deleteCell.Controls.Add(l);
                        }
                        row.Cells.Add(deleteCell);
                    }
                    #endregion

                    table.Rows.Add(row);
                }
            }
            else
            {
                TableRow row = new TableRow();
                if (!string.IsNullOrEmpty(RowCssClass))
                {
                    row.CssClass = RowCssClass;
                }
                else
                {
                    row.BorderColor = RowBorderColor;
                    row.BackColor = RowBackgroundColor;
                    table.Width = new Unit("100%");
                }

                TableCell cell = new TableCell();
                Label l = new Label();
                l.Text = Properties.Resources.ScaffoldingNoData;

                cell.Controls.Add(l);
                row.Cells.Add(cell);
                table.Rows.Add(row);
            }
            return currentEntities.Count;
        }
        #endregion

        #region Build add row
        private void BuildAddRow(Type currentEntityType, Table table)
        {
            TableRow addRow = new TableRow();
            if (!string.IsNullOrEmpty(AddRowCssClass))
            {
                addRow.CssClass = AddRowCssClass;
            }
            else
            {
                addRow.BorderStyle = BorderStyle.Solid;
                addRow.BorderWidth = new Unit(1);
                addRow.BorderColor = AddRowBorderColor;
                addRow.BackColor = AddRowBackgroundColor;
                addRow.Width = new Unit("100%");
            }
            
            if (currentEntityType != null && EntityBase.CanInsertEntity(currentEntityType))
            {
                foreach (PropertyInfo field in currentEntityType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
                {
                    object[] bindable = field.GetCustomAttributes(typeof(BindableAttribute), false);
                    if (bindable.Length > 0 &&
                        (bindable[0] as BindableAttribute).Bindable == true)
                    {
                        object[] dataObjectField = field.GetCustomAttributes(typeof(DataObjectFieldAttribute), false);
                        bool isKey = (dataObjectField.Length > 0 && (dataObjectField[0] as DataObjectFieldAttribute).PrimaryKey);

                        TableCell cell = new TableCell();
                        cell.BorderWidth = new Unit("0px");

                        Control c = null;
                        if (isKey)
                        {
                            c = new Label();
                        }
                        else
                        {
                            c = new TextBox();
                        }
                        cell.Controls.Add(c);
                        addRow.Cells.Add(cell);
                    }
                }

                TableCell addCell = new TableCell();
                if (!string.IsNullOrEmpty(CommandButtonCssClass))
                {
                    addCell.CssClass = CommandButtonCssClass;
                }
                else
                {
                    addCell.BackColor = CommandButtonBackgroundColor;
                    addCell.BorderColor = CommandButtonBorderColor;
                    addCell.BorderWidth = new Unit("1px");
                }

                LinkButton add = new LinkButton();
                add.Text = Properties.Resources.ScaffoldingAdd;
                add.Click += new EventHandler(add_Click);
                addCell.Controls.Add(add);
                addRow.Cells.Add(addCell);
            }
            else
            {
                TableCell cell = new TableCell();
                if (!string.IsNullOrEmpty(CommandButtonCssClass))
                {
                    cell.CssClass = CommandButtonCssClass;
                }
                else
                {
                    cell.BackColor = CommandButtonBackgroundColor;
                    cell.BorderColor = CommandButtonBorderColor;
                    cell.BorderWidth = new Unit("1px");
                }
                Label l = new Label();
                l.Text = Properties.Resources.ScaffoldingUnableToInsertData;
                cell.Controls.Add(l);
                addRow.Cells.Add(cell);
            }
            table.Rows.Add(addRow);
            Controls.Add(table);
        }
        #endregion

        #region Build PageRow
        private void BuildPageRow(Type currentEntityType)
        {
            if (PageCount > 1)
            {
                Panel p = new Panel();
                if (!string.IsNullOrEmpty(PagerCssClass))
                {
                }
                else
                {
                    p.Width = new Unit("100%");
                    p.BorderWidth = new Unit("1px");
                    p.BorderColor = PagerBorderColor;
                    p.BackColor = PagerBackgroundColor;
                }
                Table table = new Table();
                TableRow row = new TableRow();
                table.Rows.Add(row);
                p.Controls.Add(table);
                Controls.Add(p);

                int pageWindowWidth = PageWindow / 2;
                int pageStart = PageIndex - pageWindowWidth;
                int pageEnd = PageIndex + pageWindowWidth;
                if (pageStart < 0 && pageEnd >= PageCount)
                {
                    pageStart = 0;
                    pageEnd = PageCount;
                }
                else if (pageStart < 0)
                {
                    pageStart = 0;
                    pageEnd = pageWindowWidth * 2 + 1;
                }
                else if (pageEnd >= PageCount)
                {
                    pageEnd = PageCount;
                    pageStart = pageEnd - (pageWindowWidth * 2 + 1);
                }

                if (pageStart > 0)
                {
                    TableCell cell = new TableCell();
                    LinkButton lb = new LinkButton();
                    lb.Text = "...";
                    lb.CommandArgument = (pageStart - 1).ToString();
                    lb.Command += new CommandEventHandler(PageButton_Command);
                    cell.Controls.Add(lb);
                    row.Cells.Add(cell);
                }

                for (int x = 0; x < PageCount; x++)
                {
                    if (x >= pageStart && x < pageEnd)
                    {
                        TableCell cell = new TableCell();
                        if (x == PageIndex)
                        {
                            Label l = new Label();
                            l.Text = (x+1).ToString();
                            cell.Controls.Add(l);
                        }
                        else
                        {
                            LinkButton lb = new LinkButton();
                            lb.Text = (x+1).ToString();
                            lb.CommandArgument = x.ToString();
                            lb.Command += new CommandEventHandler(PageButton_Command);
                            cell.Controls.Add(lb);
                        }
                        row.Cells.Add(cell);
                    }
                }

                if (pageEnd < PageCount)
                {
                    TableCell cell = new TableCell();
                    LinkButton lb = new LinkButton();
                    lb.Text = "...";
                    lb.CommandArgument = pageEnd.ToString();
                    lb.Command += new CommandEventHandler(PageButton_Command);
                    cell.Controls.Add(lb);
                    row.Cells.Add(cell);
                }
            }
        }

        
        #endregion
        #endregion

        #region Cancel
        void cancelButton_Command(object sender, CommandEventArgs e)
        {
            EditItem = null;
            CreateChildControls();
        } 
        #endregion

        #region Update
        void updateButton_Command(object sender, CommandEventArgs e)
        {
            TableRow row = (sender as LinkButton).Parent.Parent as TableRow;
            if (row != null)
            {
                IEntityCollection currentEntities = CurrentEntities;
                int index = -1;
                foreach (EntityBase entity in currentEntities)
                {
                    if (entity.Equals(EditItem))
                    {
                        index = currentEntities.IndexOf(entity);
                        break;
                    }
                }
                if (index > -1)
                {
                    DatabindRowToEntity(row, currentEntities[index]);
                    if (currentEntities[index].IsValid)
                    {
                        currentEntities[index].Save();
                        EditItem = null;
                        CreateChildControls();
                    }
                    else _brokenRules = currentEntities[index].BrokenRules;
                }
            }
        }

        void editButton_Command(object sender, CommandEventArgs e)
        {
            Dictionary<string, string> keys = ParseCommandArgument(e.CommandArgument.ToString());
            EntityBase entity = FindEntity(keys);
            if (entity != null)
            {
                EditItem = entity;
                CreateChildControls();
            }
        }
        #endregion

        #region Delete
        void deleteButton_Command(object sender, CommandEventArgs e)
        {
            Dictionary<string, string> keys = ParseCommandArgument(e.CommandArgument.ToString());

            EntityBase entity = FindEntity(keys);
            if (entity != null)
            {
                entity.Delete();
                entity.Save();

                IEntityCollection currentEntities = CurrentEntities;
                currentEntities.Remove(entity);

                base.RequiresDataBinding = true;
                CreateChildControls();
            }
        }
        #endregion

        #region Helpers
        private EntityBase FindEntity(Dictionary<string, string> keys)
        {
            EntityBase entity = null;
            if (keys.Count > 0)
            {
                foreach (EntityBase en in CurrentEntities)
                {
                    bool match = false;
                    foreach (string key in keys.Keys)
                    {
                        PropertyInfo p = en.GetType().GetProperty(key);
                        if (p != null)
                        {
                            object val = p.GetValue(en, null);
                            string keyValue = keys[key];
                            object realValue = Convert.ChangeType(keyValue, val.GetType());
                            match = (val != null && realValue != null && val.Equals(realValue));
                        }
                    }
                    if (match) entity = en;
                }
            }
            return entity;
        }

        private Dictionary<string, string> ParseCommandArgument(string commandArgument)
        {
            Dictionary<string, string> keys = new Dictionary<string, string>();
            string[] args = commandArgument.Split('&');
            foreach (string arg in args)
            {
                string[] values = arg.Split('=');
                if (values.Length == 2)
                {
                    keys.Add(values[0], values[1]);
                }
            }
            return keys;
        }

        private bool DatabindRowToEntity(TableRow row, EntityBase entity)
        {
            Dictionary<string, string> keys = new Dictionary<string, string>();
            bool success = true;
            PropertyInfo[] properties = entity.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            for (int x = 0; x < properties.Length; x++)
            {
                object[] bindable = properties[x].GetCustomAttributes(typeof(BindableAttribute), false);
                if (bindable.Length > 0 &&
                    (bindable[0] as BindableAttribute).Bindable == true)
                {
                    object[] dataObjectField = properties[x].GetCustomAttributes(typeof(DataObjectFieldAttribute), false);
                    bool isKey = (dataObjectField.Length > 0 && (dataObjectField[0] as DataObjectFieldAttribute).PrimaryKey);

                    if (!isKey)
                    {
                        string val = null;
                        if (row.Controls[x].Controls[0] is TextBox)
                            val = (row.Controls[x].Controls[0] as TextBox).Text;

                        if (!string.IsNullOrEmpty(val))
                        {
                            Type t = properties[x].PropertyType;
                            if (t.IsGenericType &&
                                t.GetGenericTypeDefinition() == typeof(Nullable<>))
                                t = t.GetGenericArguments()[0];

                            object obj;
                            try
                            {
                                obj = Convert.ChangeType(val, t);
                                properties[x].SetValue(entity, obj, null);
                            }
                            catch (InvalidCastException)
                            {
                                success = false;
                            }
                            catch (FormatException)
                            {
                                success = false;
                            }

                            if (!success)
                            {
                                TableCell cell = row.Controls[x] as TableCell;
                                if (!string.IsNullOrEmpty(ErrorTableCssClass))
                                {
                                    cell.CssClass = ErrorTableCssClass;
                                }
                                else
                                {
                                    cell.BackColor = ErrorTableBackgroundColor;
                                    cell.BorderColor = ErrorTableBorderColor;
                                    cell.BorderWidth = new Unit("1px");
                                    cell.BorderStyle = BorderStyle.Solid;
                                }
                            }
                        }
                    }
                }
            }
            return success;
        }
        #endregion

        #region Add entity handler
        void add_Click(object sender, EventArgs e)
        {
            Type entityType = CurrentEntityType;

            if (entityType != null)
            {
                EntityBase entity = (EntityBase)Activator.CreateInstance(entityType);
                TableRow row = (sender as Control).Parent.Parent as TableRow;
                if (entity != null && row != null)
                {
                    if (DatabindRowToEntity(row, entity) && entity.IsValid)
                    {
                        IEntityCollection currentEntities = CurrentEntities;
                        currentEntities.Add(entity);
                        currentEntities.Save();

                        base.RequiresDataBinding = true;
                        CreateChildControls();
                    }
                    else
                    {
                        _brokenRules = entity.BrokenRules;
                    }
                }
            }
        } 
        #endregion

        #region Edit relative
        void editRelative_Command(object sender, CommandEventArgs e)
        {
            Dictionary<string, string> keys = ParseCommandArgument(e.CommandArgument.ToString());
            string relationship = keys["r"];
            keys.Remove("r");

            EntityBase entity = FindEntity(keys);
            IEntityCollection currentEntities = CurrentEntities;
            int index = currentEntities.IndexOf(entity);
            if (entity != null)
            {
                PageIndex = 0;
                ReflectionPath += "[" + index.ToString() + "]" + "." + relationship;

                CreateChildControls();
            }
        } 
        #endregion

        #region Navigate Back
        void navigateBack_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(ReflectionPath))
            {
                int index = ReflectionPath.LastIndexOf('[');
                if (index > 0)
                {
                    PageIndex = 0;
                    ReflectionPath = ReflectionPath.Substring(0, index);
                }
                else
                {
                    ReflectionPath = null;
                    base.RequiresDataBinding = true;
                }
                CreateChildControls();
            }
        }
        #endregion

        #region Paging
        void PageButton_Command(object sender, CommandEventArgs e)
        {
            int page;
            if (int.TryParse(e.CommandArgument.ToString(), out page) && page >= 0)
            {
                PageIndex = page;

                //Re fetch entities if DAL paging is performed.
                if(CurrentEntities.Count == PageSize)
                    base.RequiresDataBinding = true;

                CreateChildControls();
            }
        }
        #endregion
    }
}
